/**
 * QUnit Composite
 *
 * https://github.com/JamesMGreene/qunit-composite
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * https://jquery.org/license/
 */
/*
 * PATCHED
 * 2021-04-02  Use pushResult to silence QUnit
 *             (https://github.com/JamesMGreene/qunit-composite/pull/25)
 */
(function(factory) {
	if (typeof define === "function" && define.amd) {
		define(["qunit"], factory);
	} else {
		factory(QUnit);
	}
})(function(QUnit) {
	var iframe,
		hasBound,
		resumeTests,
		suiteAssert,
		modules = 1,
		executingComposite = false;

	function hasClass(elem, name) {
		return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
	}

	function addClass(elem, name) {
		if (!hasClass(elem, name)) {
			elem.className += (elem.className ? " " : "") + name;
		}
	}

	function addEvent(elem, type, fn) {
		if (elem.addEventListener) {
			// Standards-based browsers
			elem.addEventListener(type, fn, false);
		} else if (elem.attachEvent) {
			// support: IE <9
			elem.attachEvent("on" + type, fn);
		}
	}

	function runSuite(suite) {
		var path;

		if (QUnit.is("object", suite)) {
			path = suite.path;
			suite = suite.name;
		} else {
			path = suite;
		}

		QUnit.test(suite, function(assert) {
			resumeTests = assert.async();
			suiteAssert = assert;
			iframe.setAttribute("src", path);
			// QUnit.start is called from the child iframe's QUnit.done hook.
		});
	}

	function initIframe() {
		var iframeWin,
			body = document.body;

		function onIframeLoad() {
			var moduleName,
				testName,
				count = 0;

			if (!iframe.src) {
				return;
			}

			// Deal with QUnit being loaded asynchronously via AMD
			if (!iframeWin.QUnit && iframeWin.define && iframeWin.define.amd) {
				return iframeWin.require(["qunit"], onIframeLoad);
			}

			iframeWin.QUnit.moduleStart(function(data) {
				// Capture module name for messages
				moduleName = data.name;
			});

			iframeWin.QUnit.testStart(function(data) {
				// Capture test name for messages
				testName = data.name;
			});
			iframeWin.QUnit.testDone(function() {
				testName = undefined;
			});

			iframeWin.QUnit.log(function(data) {
				if (testName === undefined) {
					return;
				}
				// Pass all test details through to the main page
				var message =
					(moduleName ? moduleName + ": " : "") +
					testName +
					": " +
					(data.message || (data.result ? "okay" : "failed"));
				suiteAssert.expect(++count);
				// PATCHED to silence QUnit (https://github.com/JamesMGreene/qunit-composite/pull/25)
				suiteAssert.pushResult({
					result: data.result,
					actual: data.actual,
					expected: data.expected,
					message: message,
				});
			});

			// Continue the outer test when the iframe's test is done
			iframeWin.QUnit.done(function() {
				resumeTests();
			});
		}

		iframe = document.createElement("iframe");
		iframe.className = "qunit-composite-suite";
		body.appendChild(iframe);

		addEvent(iframe, "load", onIframeLoad);

		iframeWin = iframe.contentWindow;
	}

	function appendSuitesToHeader(suites) {
		var i,
			suitesLen,
			suite,
			path,
			name,
			suitesEl,
			testResultEl,
			newSuiteListItemEl,
			newSuiteLinkEl;

		suitesEl = document.getElementById("qunit-testsuites");

		if (!suitesEl) {
			testResultEl = document.getElementById("qunit-testresult");

			if (!testResultEl) {
				// QUnit has not been set up yet. Defer until QUnit is ready.
				QUnit.begin(function() {
					appendSuitesToHeader(suites);
				});
				return;
			}

			suitesEl = document.createElement("ul");
			suitesEl.id = "qunit-testsuites";
			testResultEl.parentNode.insertBefore(suitesEl, testResultEl);
		}

		for (i = 0, suitesLen = suites.length; i < suitesLen; ++i) {
			suite = suites[i];
			newSuiteLinkEl = document.createElement("a");
			newSuiteLinkEl.innerHTML = suite.name || suite;
			newSuiteLinkEl.href = suite.path || suite;

			newSuiteListItemEl = document.createElement("li");
			newSuiteListItemEl.appendChild(newSuiteLinkEl);

			suitesEl.appendChild(newSuiteListItemEl);
		}
	}

	/**
	 * @param {string} [name] Module name to group these test suites.
	 * @param {Array} suites List of suites where each suite
	 *  may either be a string (path to the html test page),
	 *  or an object with a path and name property.
	 */
	QUnit.testSuites = function(name, suites) {
		var i, suitesLen;

		if (arguments.length === 1) {
			suites = name;
			name = "Composition #" + modules++;
		}
		suitesLen = suites.length;

		appendSuitesToHeader(suites);

		if (!hasBound) {
			hasBound = true;
			QUnit.begin(initIframe);

			// TODO: Would be better to use something like QUnit.once( 'moduleDone' )
			// after the last test suite.
			QUnit.moduleDone(function() {
				executingComposite = false;
			});

			QUnit.done(function() {
				iframe.style.display = "none";
			});
		}

		QUnit.module(name, {
			beforeEach: function() {
				executingComposite = true;
			},
		});

		for (i = 0; i < suitesLen; i++) {
			runSuite(suites[i]);
		}
	};

	QUnit.testDone(function(data) {
		if (!executingComposite) {
			return;
		}

		var i,
			len,
			testId = data.testId,
			current = document.getElementById("qunit-test-output-" + testId),
			children = current && current.children,
			src = iframe.src;

		if (!(current && children)) {
			return;
		}

		addEvent(current, "dblclick", function(e) {
			var target = e && e.target ? e.target : window.event.srcElement;
			if (
				target.nodeName.toLowerCase() === "span" ||
				target.nodeName.toLowerCase() === "b"
			) {
				target = target.parentNode;
			}
			if (window.location && target.nodeName.toLowerCase() === "strong") {
				window.location = src;
			}
		});

		// Undo QUnit's auto-expansion for bad tests
		for (i = 0, len = children.length; i < len; i++) {
			if (children[i].nodeName.toLowerCase() === "ol") {
				addClass(children[i], "qunit-collapsed");
			}
		}

		// Update Rerun link to point to the standalone test suite page
		current.getElementsByTagName("a")[0].href = src;
	});
});
